package com.cc.bsp.persistence.hibernate;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;

import org.hibernate.Query;
import org.springframework.util.Assert;

import com.cc.bsp.persistence.IQueryPageOrder;
import com.cc.bsp.persistence.entity.IEntity;
import com.cc.bsp.persistence.exception.PersistenceException;
import com.cc.bsp.persistence.util.BeansUtil;

public abstract class AbstractEntityXQLDao<T extends IEntity,ID extends Serializable>extends AbstractEntityDao<T,ID> implements IHXQLDao{

	public AbstractEntityXQLDao(Class<T> clazzz) {
		super(clazzz);
		
	}
	
	@SuppressWarnings("unchecked")
	public <X> X findUniqueByHQL(String hql, Object... propertyParameters) throws PersistenceException {
			Assert.hasLength(hql);
			Assert.noNullElements(propertyParameters);
		   return (X) createHQLQuery(hql, null, propertyParameters).uniqueResult();  
	}

	@SuppressWarnings("unchecked")
	public <X> X findUniqueBySQL(String sql, Object... propertyParameters) throws PersistenceException {
		  Assert.hasLength(sql);
		  Assert.noNullElements(propertyParameters);
		  return (X) createSQLQuery(sql, null, propertyParameters).uniqueResult(); 
	}

	@SuppressWarnings("unchecked")
	public <X> List<X> findByHQL(String hql, Object... propertyParameters) throws PersistenceException {
		Assert.hasLength(hql);
		Assert.noNullElements(propertyParameters);
		return createHQLQuery(hql, null, propertyParameters).list();
	}

	@SuppressWarnings("unchecked")
	public <X> List<X> findBySQL(String sql, Object... propertyParameters) throws PersistenceException {
		Assert.hasLength(sql);
		Assert.noNullElements(propertyParameters);
		return createSQLQuery(sql, null, propertyParameters).list();
	}

	@SuppressWarnings("unchecked")
	public <X> List<X> findByNamedQL(String namedQL, final IHRootDao.TYPE type, Object... javaBeans)
			throws PersistenceException {
		Assert.hasLength(namedQL);
		Assert.notNull(type);
		Assert.noNullElements(javaBeans);
		
		Map<String, Object> parameters = BeansUtil.beans2Map(javaBeans);
	
		if(IHRootDao.TYPE.HQL==type){
			String ql = statementTemplate.getHQL(namedQL,parameters);
			if(null == ql || "".equals(ql.trim())){
				throw new PersistenceException("findByNamedQL() error : the namedQL is not found");
			}
			
			return createHQLQuery(ql, null,parameters).list();
		}else if(IHRootDao.TYPE.SQL==type){
			String ql = statementTemplate.getSQL(namedQL,parameters);
			if(null == ql || "".equals(ql.trim())){
				throw new PersistenceException("findByNamedQL() error : the namedQL is not found");
			}
			return createSQLQuery(ql, null,parameters).list();
		}

		return null;
	}

	@SuppressWarnings("unchecked")
	public <X> List<X> findByNamedQL(String namedQL,final IHRootDao.TYPE type,  Map<String, ?> parameters)
			throws PersistenceException {
		
		Assert.hasLength(namedQL);
		Assert.notNull(type);
		Assert.notEmpty(parameters);
		
		if(IHRootDao.TYPE.HQL==type){
			String ql = statementTemplate.getHQL(namedQL,parameters);
			return createHQLQuery(ql, null,parameters).list();
		}else if(IHRootDao.TYPE.SQL==type){
			String ql = statementTemplate.getSQL(namedQL,parameters);
			return createSQLQuery(ql, null,parameters).list();
		}
		return null;
	}

	public void batchExecuteHQL(String hql, Object... propertyParameters)throws PersistenceException {
		Assert.hasLength(hql);
	
		Assert.noNullElements(propertyParameters);
		
		 createHQLQuery(hql, null, propertyParameters).executeUpdate(); 
		
	}

	public void batchExecuteSQL(String sql, Object... propertyParameters)throws PersistenceException {
		Assert.hasLength(sql);
		
		Assert.noNullElements(propertyParameters);
		 createSQLQuery(sql, null, propertyParameters).executeUpdate(); 
	}

	public void batchExecuteHQL(String hql, Map<String, ?> parameters)throws PersistenceException {
		Assert.hasLength(hql);
		
		Assert.notEmpty(parameters);
		 createSQLQuery(hql,  null,parameters).executeUpdate(); 
		
	}

	public void batchExecuteSQL(String sql, Map<String, ?> parameters) throws PersistenceException {
		Assert.hasLength(sql);
		
		Assert.notEmpty(parameters);
		 createSQLQuery(sql, null,parameters).executeUpdate(); 
		
	}
	
	@SuppressWarnings("unchecked")
	public <X> List<X> findByHQL(String hql, IQueryPageOrder pageorder,
			Object... propertyParameters) throws PersistenceException {
		Assert.hasLength(hql);
		Assert.notNull(pageorder);
		Assert.noNullElements(propertyParameters);
		
		return createHQLQuery(hql, pageorder, propertyParameters).list();
	}

	@SuppressWarnings("unchecked")
	public <X> List<X> findBySQL(String sql, IQueryPageOrder pageorder,
			Object... propertyParameters) throws PersistenceException {
		Assert.hasLength(sql);
		Assert.notNull(pageorder);
		Assert.noNullElements(propertyParameters);
		
		return createSQLQuery(sql, pageorder, propertyParameters).list();
	}

	@SuppressWarnings("unchecked")
	public <X> List<X> findByNamedQL(String namedQL, IHRootDao.TYPE type,
			IQueryPageOrder pageorder, Object... javaBeans) throws PersistenceException {
		Assert.hasLength(namedQL);
		Assert.notNull(type);
		Assert.notNull(pageorder);
		Assert.noNullElements(javaBeans);
		
		Map<String, Object> parameters = BeansUtil.beans2Map(javaBeans);
		if(IHRootDao.TYPE.HQL==type){
			String ql = statementTemplate.getHQL(namedQL,parameters);
			return createHQLQuery(ql, pageorder,parameters).list();
		}else if(IHRootDao.TYPE.SQL==type){
			String ql = statementTemplate.getSQL(namedQL,parameters);
			return createSQLQuery(ql, pageorder,parameters).list();
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	public <X> List<X> findByNamedQL(String namedQL, IHRootDao.TYPE type,
			IQueryPageOrder pageorder, Map<String, ?> parameters)
			throws PersistenceException {
		Assert.hasLength(namedQL);
		Assert.notNull(type);
		Assert.notNull(pageorder);
		Assert.notEmpty(parameters);
		
		if(IHRootDao.TYPE.HQL==type){
			String ql = statementTemplate.getHQL(namedQL,parameters);
			return createHQLQuery(ql, pageorder,parameters).list();
		}else if(IHRootDao.TYPE.SQL==type){
			String ql = statementTemplate.getSQL(namedQL,parameters);
			return createSQLQuery(ql, pageorder,parameters).list();
		}
		return null;
	}
	

	Query createHQLQuery(final String queryString,IQueryPageOrder pageorder,final Object... propertyParameters) {
		Assert.hasLength(queryString);
		Assert.notNull(pageorder);
		Assert.noNullElements(propertyParameters);

		if (pageorder != null && pageorder.getPage() != null) {
			String countHQL = getCountQL(queryString);

			Query queryCount = getSession().createQuery(countHQL);

			if (propertyParameters != null) {
				for (int i = 0; i < propertyParameters.length; i++) {
					queryCount.setParameter(i, propertyParameters[i]);
				}
			}

			long total = ((Long) queryCount.iterate().next()).intValue();

			pageorder.createPage(total);
		}

		Query query = createQueryXQLQuery(queryString,IHRootDao.TYPE.HQL,pageorder);
		if (propertyParameters != null) {
			for (int i = 0; i < propertyParameters.length; i++) {
				query.setParameter(i, propertyParameters[i]);
			}
		}
		
		return query;
	}
	

	Query createHQLQuery(final String queryString,IQueryPageOrder pageorder,final Map<String, ?> parameters) {
		Assert.hasLength(queryString);
		Assert.notNull(pageorder);
		Assert.notEmpty(parameters);
		
		if (pageorder != null && pageorder.getPage() != null) {
			String countHQL = getCountQL(queryString);

			Query queryCount = getSession().createQuery(countHQL);

			queryCount.setProperties(parameters);

			long total = ((Long) queryCount.iterate().next()).intValue();

			pageorder.createPage(total);

		}

		Query query = createQueryXQLQuery(queryString,IHRootDao.TYPE.HQL,pageorder);
		
		query.setProperties(parameters);
		
		return query;
	}
	
	Query createSQLQuery(final String queryString,IQueryPageOrder pageorder,final Object... propertyParameters) {
		Assert.hasLength(queryString);
		Assert.notNull(pageorder);
		Assert.noNullElements(propertyParameters);
		if (pageorder != null && pageorder.getPage() != null) {
			String countHQL = getCountQL(queryString);

			Query queryCount = getSession().createSQLQuery(countHQL);

			if (propertyParameters != null) {
				for (int i = 0; i < propertyParameters.length; i++) {
					queryCount.setParameter(i, propertyParameters[i]);
				}
			}

			BigInteger total = ((BigInteger) queryCount.uniqueResult());

			pageorder.createPage(total.longValue());
		}

		
		Query query = createQueryXQLQuery(queryString,IHRootDao.TYPE.SQL,pageorder);
		if (propertyParameters != null) {
			for (int i = 0; i < propertyParameters.length; i++) {
				query.setParameter(i, propertyParameters[i]);
			}
		}
		return query;
	}
	
	Query createSQLQuery(final String queryString,IQueryPageOrder pageorder,final Map<String, ?> parameters) {
		Assert.hasLength(queryString);
		Assert.notNull(pageorder);
		Assert.notEmpty(parameters);
		if (pageorder != null && pageorder.getPage() != null) {
			String countHQL = getCountQL(queryString);

			Query queryCount = getSession().createSQLQuery(countHQL);

			queryCount.setProperties(parameters);

			BigInteger total = ((BigInteger) queryCount.uniqueResult());

			pageorder.createPage(total.longValue());
		}
		
		Query query = createQueryXQLQuery(queryString,IHRootDao.TYPE.SQL,pageorder);
		
		query.setProperties(parameters);
	
		return query;
	}

	private String getCountQL(String ql){
		Assert.hasLength(ql);
	
		String initQL1 = new String(ql);
		
		String initQL2 = new String(ql);
		
		String upperQL = initQL1.toUpperCase();
		
		int indexFrom = upperQL.indexOf("FROM");
	
		int indexCount = upperQL.indexOf("COUNT(*)");
		
		if (indexCount >= 0 && indexCount < indexFrom) {
			return ql;
		}
		
		String endFrom = "";
		
		if(indexFrom >= 0){
			
			endFrom = initQL2.substring(indexFrom);
		}
		
		String resultQL = "select count(*) " + endFrom;
		
		return resultQL;
	}

}
